class Compiler {
  constructor(vm){
    this.el = vm.$el
    this.vm = vm

    this.compile(this.el)
  }

  // 编译模板，处理文本节点和元素节点
  compile(el){
    // children是子元素，childNodes是子节点,节点带插值的。伪数组
    // forEach只遍历了第一层子节点
    Array.from(el.childNodes).forEach(node =>{
      if(this.isTextNode(node)){
        this.compileText(node)
      } else if(this.isElementNode(node)){
        this.compileElement(node)
      }

      // 递归子节点
      if(node.childNodes && node.childNodes.length){
        this.compile(node)
      }
    })
  }

  // 编译元素节点，处理指令
  compileElement(node){
    // 伪数组，元素节点的属性 (伪数组转化，Array.from,[...xx])
    [...node.attributes].forEach(attr =>{
      const {name,value} = attr
      if(this.isDirective(name)){ // 是 v-on:click v-text 指令
        const [,commandName] = name.split("-") // text on:click 
        const [dirname,eventName] = commandName.split(":") // [on,click] [text,undefined]
        complieUtil[dirname](node,value,this.vm,eventName)
        node.removeAttribute('v-'+commandName)
      }else if(this.isEventName(name)){
        const [,eventName] = name.split("@") // [@,click]
        complieUtil['on'](node,value,this.vm,eventName)
      }
    })
  }

  // 编译文本节点，处理插值表达式
  compileText(node){
    // console.log(node) // 节点中间的内容 没内容为#text（回车）
    // console.dir(node) // 以对象形式打印出来

    // .任意字符， +前面内容1/多次 ?非贪婪模式，尽早结束 ()分组提取内容
    let reg = /\{\{(.+?)\}\}/  
    let content = node.textContent
    if(reg.test(content)){
      let key = RegExp.$1.trim() // 第一组
      let val = complieUtil.getVal(key,this.vm)
      node.textContent = content.replace(reg,val)

      // 创建watcher对象
      new Watcher(this.vm,key,(newVal)=>{
        node.textContent = newVal
      })
    }
  }

  // 判断元素是否是指令
  isDirective(attrName){
    return attrName.startsWith('v-')
  }

  isEventName(attrName){
    return attrName.startsWith('@')
  }

  // 判断节点是否是文本节点
  // https://developer.mozilla.org/zh-CN/docs/Web/API/Node/nodeType
  isTextNode(node){
    return node.nodeType === 3
  }

  // 判断节点是否是元素节点
  isElementNode(node){
    return node.nodeType === 1
  }
}